use crate::elf::elf_types::*;
use nom::lib::std::collections::BTreeMap;
use crate::elf::elf_enum::*;

use memmap::{MmapOptions, Mmap};
use std::fs::File;
use failure::Error;

pub struct Elf32File {
    pub ehdr: Elf32_Ehdr,
    pub phdrs: Vec<Elf32_Phdr>,
    pub dynamics: BTreeMap<DynamicTag, Vec<Elf32_Dynamic>>,

    pub preinit_functor: Option<Vec<Elf32_Functor>>,
    pub init_functor: Option<Vec<Elf32_Functor>>,
    pub finit_functor: Option<Vec<Elf32_Functor>>,
    // pub dynamic_symbol: Vec<Elf32_Symbol>,
    // pub relocs: Option<Vec<Elf32_Relocation>>,
    // pub relocas: Option<Vec<Elf32_RelocationA>>,

    symtab_offset: u32,
    is_reloc_addend: bool,
    relocs_offset: u32,

    elf_mmap: Mmap,
    strings_offset: u32,
    strings_size:u32,

}

fn get_dynamic_val(dynamics: &BTreeMap<DynamicTag, Vec<Elf32_Dynamic>>, tag: &DynamicTag) -> Option<u32> {
    dynamics.get(tag)
        .and_then(|tag_dynamics| {
            tag_dynamics.get(0).and_then(|dynamic|{
                Some(dynamic.d_val)
            })
        })
}

fn get_functor_array(elf_mmap :&Mmap, dynamics: &BTreeMap<DynamicTag, Vec<Elf32_Dynamic>>,
                     functor_tag: &DynamicTag, functor_sz_tag: &DynamicTag) -> Option<Vec<Elf32_Functor>> {
    let mut functor_array = Option::None;
    if let Some(preinit_offset) = get_dynamic_val(&dynamics, functor_tag) {
        let functor_bytes = &elf_mmap[preinit_offset as usize..];
        let mut functors = Vec::<Elf32_Functor>::new();

        let mut functor_count = 0;
        if let Some(count) = get_dynamic_val(dynamics, functor_sz_tag) {
            functor_count = (count / 4) as u32;
        }

        let mut step: u32 = 0;
        loop {
            match parser_elf32_functor(&functor_bytes[(step * 4) as usize..]) {
                  Ok((_, elf32_functor)) => { functors.push(elf32_functor); },
                  Err(_e) => {}
            };
            step += 1;
            if step > functor_count  {
                break;
            }
        }

        functor_array = Some(functors);
    }
    functor_array
}

pub fn parser_elf_file(elf_path: &str) -> Result<Elf32File, Error>{
    let elf_file = File::open(elf_path)?;
    let elf_mmap = unsafe{ MmapOptions::new().map(&elf_file)? };
    let  ehdr= parser_elf32_ehdr(&elf_mmap)
        .map_err(|_| failure::format_err!("Failed to parser Ehdr"))?.1;



    let phdr_bytes = &elf_mmap[ELF32_EHDR_SIZE..];
    let mut phdrs = Vec::<Elf32_Phdr> ::new();
    for i in 0..ehdr.e_phnum as usize {
        let phdr = parser_elf32_phdr(&phdr_bytes[i * ELF32_PHDR_SIZE..])
            .map_err(|_| failure::format_err!("Failed to parser Phdr"))?.1;
        phdrs.push(phdr);
    }

    let mut dyn_phdr: &Elf32_Phdr = &Default::default();
    if let Some(phdr) = phdrs.iter()
        .find(|&&phdr| SegmentType::PT_DYNAMIC == phdr.p_type.into()) {
        dyn_phdr = phdr;
    } else {
        println!("Cannot find the Segment Header for PT_DYNAMIC");
    };

    let mut dynamic_in_load_segment = &Default::default();
    if let Some(phdr) =  phdrs.iter()
        .find(|&&phdr| phdr.p_vaddr < dyn_phdr.p_vaddr && dyn_phdr.p_vaddr < phdr.p_vaddr + phdr.p_memsz) {
        dynamic_in_load_segment = phdr;
    } else {
        println!("Cannot find the Segment Header for PT_DYNAMIC");
    };

    let mut dynamics = BTreeMap::<DynamicTag, Vec<Elf32_Dynamic>>::new();
    let dyn_bytes = &elf_mmap[dyn_phdr.p_offset as usize..];
    let mut cost_size = 0 as usize;
    loop {
        let dynamic = parser_elf32_dynamic(&dyn_bytes[cost_size..])
            .map_err(|_| failure::format_err!("Failed to parser the dynamic"))?.1;
        if let Some(t_dynamics) = dynamics.get_mut(&dynamic.d_tag.into()) {
            t_dynamics.push(dynamic);
        } else {
            let mut t_dyanmics = Vec::<Elf32_Dynamic>::new();
            t_dyanmics.push(dynamic);
            dynamics.insert(dynamic.d_tag.into(), t_dyanmics);
        }

        cost_size += ELF32_DYNAMIC_SIZE;
        if cost_size >= dyn_phdr.p_filesz as usize {
            break;
        }
    }

    let mut symtab_offset = 0;
    if let Some(offset) = get_dynamic_val(&dynamics, &DynamicTag::DT_SYMTAB) {
        symtab_offset = offset;
    } else {
        failure::format_err!("Cannot find the symtabs....");
    }

    if let Some(map_offset) = get_dynamic_val(&dynamics, &DynamicTag::DT_STRTAB) {
        strings_offset = map_offset - dynamic_in_load_segment.p_vaddr;
    }

    if let Some(size) = get_dynamic_val(&dynamics, &DynamicTag::DT_STRSZ) {
        strings_size = size;
    }

    let preinit_functor = get_functor_array(&elf_mmap, &dynamics,
                                            &DynamicTag::DT_PREINIT_ARRAY,
                                            &DynamicTag::DT_PREINIT_ARRAYSZ);

    let init_functor = get_functor_array(&elf_mmap, &dynamics,
                                            &DynamicTag::DT_INIT_ARRAY,
                                            &DynamicTag::DT_INIT_ARRAYSZ);


    let finit_functor = get_functor_array(&elf_mmap, &dynamics,
                                         &DynamicTag::DT_FINI_ARRAY,
                                         &DynamicTag::DT_FINI_ARRAYSZ);

    Ok(Elf32File {
        ehdr,
        phdrs,
        dynamics,
        preinit_functor,
        init_functor,
        finit_functor,
        symtab_offset,
        is_reloc_addend: false,
        relocs_offset: 0,
        elf_mmap,
        strings_offset,
        strings_size
    })

    
}